package test.marui.com.mytest.view.layerView.view;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.AnimRes;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.FloatRange;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;

import test.marui.com.mytest.R;
import test.marui.com.mytest.utils.BitmapUtil;
import test.marui.com.mytest.utils.BlurUtil;
import test.marui.com.mytest.utils.CommonUtil;

/**
 * Created by MR on 2019/1/21.
 */
public class AnyLayer implements LayerManager.LiveListener {

    private Context mContext;
    private LayoutInflater mInflater;
    private ViewGroup mRootView;
    private View mTargetView;
    private LayerManager mLayerManager;
    private ViewHolder mViewHolder;

    private int mGravity = Gravity.CENTER;
    private Direction mDirection;
    private IAnim mContentAnim;

    private Animation mContentInAnim;
    private Animation mContentOutAnim;

    private long mContentDuration = 300;
    private long mBackgroundDuration = 300;

    private IAnim mBackgroundAnim;
    private Animation mBackgroundInAnim;
    private Animation mBackgroundOutAnim;
    private IDataBinder mDataBinder;
    private View mContentView;
    private int mBackgroundColor = Color.TRANSPARENT;
    private float mBackgroundBlurRadius;
    private float mBackgroundBlurScale = 0;
    private Bitmap mBackgroundBitmap;
    private Drawable mBackgroundDrawable;
    private int mBackgroundResource = 0;
    private boolean mCancelAbleOnTouchOutSide;
    private OnVisibleChangeListener mOnVisibleChangeListener;
    private OnLayerShowListener mOnLayerShowListener;
    private OnLayerDismissListener mOnLayerDismissListener;

    private AnyLayer(Context context) {
        mContext = context;
        mInflater = LayoutInflater.from(context);
        initRootView();
        initView();
    }

    private AnyLayer(ViewGroup viewGroup) {
        mContext = viewGroup.getContext();
        mInflater = LayoutInflater.from(mContext);
        mRootView = viewGroup;
        initView();
    }

    private AnyLayer(View targetView) {
        mContext = targetView.getContext();
        mInflater = LayoutInflater.from(mContext);
        mTargetView = targetView;
        initRootView();
        initView();
    }

    private void initRootView() {
        Activity activity = CommonUtil.getActivity(mContext);
        if (activity == null) {
            throw new NullPointerException();
        }

        mRootView = activity.getWindow().getDecorView().findViewById(android.R.id.content);
    }

    private void initView() {
        FrameLayout container = (FrameLayout) mInflater.inflate(R.layout.layout_any_layer, mRootView, false);
        mLayerManager = new LayerManager(mRootView, container);
        mViewHolder = new ViewHolder(this, container);
        mLayerManager.setLiveListener(this);
    }

    public void show() {
        mLayerManager.add();
    }

    public void dismiss() {
        mLayerManager.remove();
    }

    //region 初始化AnyLayer
    public static AnyLayer with(Context context) {
        return new AnyLayer(context);
    }

    public static AnyLayer with(ViewGroup viewGroup) {
        return new AnyLayer(viewGroup);
    }

    public static AnyLayer with(View targetView) {
        return new AnyLayer(targetView);
    }
    //endregion

    @Override
    public void onAttach() {
        initContainer();
        initBackground();
        initContent();
        mViewHolder.bindClick();
        if (mOnVisibleChangeListener != null) {
            mOnVisibleChangeListener.onShow(this);
        }

        if (mDataBinder != null) {
            mDataBinder.bind(this);
        }

        if (mOnLayerShowListener != null) {
            mOnLayerShowListener.onShowing(this);
        }
    }

    private void initContent() {

        if (mContentView != null) {
            ViewGroup parent = (ViewGroup) mContentView.getParent();
            if (parent != null) {
                parent.removeView(mContentView);
            }
            mContentView.setClickable(true);

            if (mGravity != -1) {
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mContentView.getLayoutParams();
                params.gravity = mGravity;
                mContentView.setLayoutParams(params);
            }

            mViewHolder.getmContentWrapper().addView(mContentView);
        }

    }

    private void initBackground() {
        if (mBackgroundBlurRadius > 0) {
            mViewHolder.getmIvBackground().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    if (mViewHolder.getmIvBackground().getViewTreeObserver().isAlive()) {
                        mViewHolder.getmIvBackground().getViewTreeObserver().removeOnPreDrawListener(this);
                        Bitmap snapshot = BitmapUtil.snapshot(mRootView);

                        int[] locationRoot = new int[2];
                        mRootView.getLocationOnScreen(locationRoot);
                        int[] location = new int[2];
                        mViewHolder.getmIvBackground().getLocationOnScreen(location);
                        Bitmap originalBitmap = Bitmap.createBitmap(snapshot, location[0]-locationRoot[0], location[1]-locationRoot[1], mViewHolder.getmIvBackground().getWidth(), mViewHolder.getmIvBackground().getHeight());
                        snapshot.recycle();
                        Bitmap blurBitmap = BlurUtil.blur(mContext, originalBitmap, mBackgroundBlurRadius, mBackgroundBlurScale);
                        originalBitmap.recycle();
                        mViewHolder.getmIvBackground().setScaleType(ImageView.ScaleType.FIT_XY);
                        mViewHolder.getmIvBackground().setImageBitmap(blurBitmap);
                        mViewHolder.getmIvBackground().setBackgroundColor(mBackgroundColor);
                    }
                    return true;
                }
            });
        } else {
            if (mBackgroundBitmap != null) {
                mViewHolder.getmIvBackground().setImageBitmap(mBackgroundBitmap);
                mViewHolder.getmIvBackground().setColorFilter(mBackgroundColor);
            } else if (mBackgroundDrawable != null) {
                mViewHolder.getmIvBackground().setImageDrawable(mBackgroundDrawable);
                mViewHolder.getmIvBackground().setColorFilter(mBackgroundColor);
            } else if (mBackgroundResource != 0) {
                mViewHolder.getmIvBackground().setImageResource(mBackgroundResource);
                mViewHolder.getmIvBackground().setColorFilter(mBackgroundColor);
            } else if (mBackgroundColor != Color.TRANSPARENT) {
                mViewHolder.getmIvBackground().setImageDrawable(new ColorDrawable(mBackgroundColor));
            }
        }

    }

    private void initContainer() {
        if (mCancelAbleOnTouchOutSide) {
            mViewHolder.getmContainer().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dismiss();
                }
            });
        }

        if (mTargetView != null) {
            int[] locationTarget = new int[2];
            mTargetView.getLocationOnScreen(locationTarget);
            int[] locationRoot = new int[2];
            mRootView.getLocationOnScreen(locationRoot);

            int paddingTop = 0;
            int paddingBottom = 0;
            int paddingRight = 0;
            int paddingLeft = 0;
            if (mDirection == Direction.TOP) {
                paddingBottom = mRootView.getHeight() - locationTarget[1] + locationRoot[1];
            } else if (mDirection == Direction.BOTTOM) {
                paddingTop = locationTarget[1] + mTargetView.getHeight() - locationRoot[1];
            } else if (mDirection == Direction.LEFT) {
                paddingRight = mRootView.getWidth() - locationTarget[0] + locationRoot[0];
            } else if (mDirection == Direction.RIGHT) {
                paddingLeft = locationTarget[0] + mTargetView.getWidth() - locationRoot[0];
            }
            mViewHolder.getmContainer().setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
        }
    }

    @Override
    public void onShow() {
        if (mOnLayerShowListener != null) {
            mOnLayerShowListener.onShown(this);
        }
    }

    @Override
    public long onAnimIn(View view) {
        startContentInAnim();
        startBackgroundInAnim();
        return getDuration();
    }

    @Override
    public void onRemove() {
        if (mOnLayerDismissListener != null) {
            mOnLayerDismissListener.onDismissing(this);
        }
    }

    @Override
    public long onAnimOut(View view) {
        startContentOutAnim();
        startBackgroundOutAnim();
        return getDuration();
    }

    @Override
    public void onDetach() {

        if (mOnVisibleChangeListener != null) {
            mOnVisibleChangeListener.onDismiss(this);
        }

        if (mOnLayerDismissListener != null) {
            mOnLayerDismissListener.onDismissed(this);
        }
        mViewHolder.recycle();
    }


    private long getDuration() {
        return Math.max(mContentDuration, mBackgroundDuration);
    }

    private void startContentInAnim() {
        if (mContentAnim != null) {
            mContentDuration = mContentAnim.inAnim(mContentView);
        } else {
            if (mContentInAnim != null) {
                mContentView.startAnimation(mContentInAnim);
            } else {
                AnimHelper.startZoomInAnim(mContentView, mContentDuration);
            }
        }
    }

    private void startBackgroundInAnim() {
        if (mBackgroundAnim != null) {
            mBackgroundDuration = mBackgroundAnim.inAnim(mViewHolder.getmIvBackground());
        } else {
            if (mBackgroundInAnim != null) {
                mViewHolder.getmIvBackground().startAnimation(mBackgroundInAnim);
            } else {
                AnimHelper.startAlphaInAnim(mViewHolder.getmIvBackground(), mBackgroundDuration);
            }
        }
    }

    private void startContentOutAnim() {
        if (mContentAnim != null) {
            mContentDuration = mContentAnim.outAnim(mContentView);
        } else {
            if (mContentOutAnim != null) {
                mContentView.startAnimation(mContentOutAnim);
            } else {
                AnimHelper.startZoomOutAnim(mContentView, mContentDuration);
            }
        }
    }

    private void startBackgroundOutAnim() {
        if (mBackgroundAnim != null) {
            mBackgroundDuration = mBackgroundAnim.outAnim(mViewHolder.getmIvBackground());
        } else {
            if (mBackgroundOutAnim != null) {
                mViewHolder.getmIvBackground().startAnimation(mBackgroundOutAnim);
            } else {
                AnimHelper.startAlphaOutAnim(mViewHolder.getmIvBackground(), mBackgroundDuration);
            }
        }
    }

    //region 可设置属性
    public AnyLayer gravity(int gravity) {
        mGravity = gravity;
        return this;
    }

    public AnyLayer direction(Direction direction) {
        mDirection = direction;
        return this;
    }

    public AnyLayer contentAnim(IAnim iAnim) {
        mContentAnim = iAnim;
        return this;
    }

    public AnyLayer contentInAnim(@AnimRes int anim) {
        contentInAnim(AnimationUtils.loadAnimation(mContext, anim));
        return this;
    }

    public AnyLayer contentInAnim(@NonNull Animation animation) {
        mContentInAnim = animation;
        mContentDuration = Math.max(mContentDuration, mContentInAnim.getDuration());
        return this;
    }

    public AnyLayer contentOutAnim(@AnimRes int anim) {
        contentOutAnim(AnimationUtils.loadAnimation(mContext, anim));
        return this;
    }

    public AnyLayer contentOutAnim(@NonNull Animation animation) {
        mContentOutAnim = animation;
        mContentDuration = Math.max(mContentDuration, mContentOutAnim.getDuration());
        return this;
    }

    public AnyLayer backgroundAnim(IAnim iAnim) {
        mBackgroundAnim = iAnim;
        return this;
    }

    public AnyLayer backgroundInAnim(@AnimRes int anim) {
        backgroundInAnim(AnimationUtils.loadAnimation(mContext, anim));
        return this;
    }

    public AnyLayer backgroundInAnim(@NonNull Animation animation) {
        mBackgroundInAnim = animation;
        mBackgroundDuration = Math.max(mBackgroundDuration, mBackgroundInAnim.getDuration());
        return this;
    }

    public AnyLayer backgroundOutAnim(@AnimRes int anim) {
        backgroundOutAnim(AnimationUtils.loadAnimation(mContext, anim));
        return this;
    }

    public AnyLayer backgroundOutAnim(@NonNull Animation animation) {
        mBackgroundOutAnim = animation;
        mBackgroundDuration = Math.max(mBackgroundDuration, mBackgroundOutAnim.getDuration());
        return this;
    }

    public AnyLayer defaultContentAnimDuration(long duration) {
        mContentDuration = duration;
        return this;
    }

    public AnyLayer defaultBackgroundAnimDuration(long duration) {
        mBackgroundDuration = duration;
        return this;
    }

    public AnyLayer bindData(IDataBinder dataBinder) {
        mDataBinder = dataBinder;
        return this;
    }

    public AnyLayer contentView(@NonNull View view) {
        mContentView = view;
        return this;
    }

    public AnyLayer contentView(@LayoutRes int id) {
        mContentView = mInflater.inflate(id, mViewHolder.getmContainer(), false);
        return this;
    }

    public AnyLayer backgroundColorInt(@ColorInt int colorInt) {
        mBackgroundColor = colorInt;
        return this;
    }

    public AnyLayer backgroundColorRes(@ColorRes int colorRes) {
        mBackgroundColor = ContextCompat.getColor(mContext, colorRes);
        return this;
    }

    public AnyLayer backgroundBlurRadius(@FloatRange(from = 0, fromInclusive = false, to = 25) float radius) {
        mBackgroundBlurRadius = radius;
        return this;
    }

    public AnyLayer backgroundBlurScale(@FloatRange(from = 1) float scale) {
        mBackgroundBlurScale = scale;
        return this;
    }

    public AnyLayer backgroundBitmap(@NonNull Bitmap bitmap) {
        mBackgroundBitmap = bitmap;
        return this;
    }

    public AnyLayer backgroundDrawable(@NonNull Drawable drawable) {
        mBackgroundDrawable = drawable;
        return this;
    }

    public AnyLayer backgroundResource(@DrawableRes int id) {
        mBackgroundResource = id;
        return this;
    }

    public AnyLayer cancelableOnTouchOutSide(boolean able) {
        mCancelAbleOnTouchOutSide = able;
        return this;
    }

    public AnyLayer cancelableOnClickKeyBack(boolean able) {
        mLayerManager.setCancelableOnClickKeyBack(able);
        return this;
    }

    public AnyLayer onVisibleChangeListener(OnVisibleChangeListener mOnVisibleChangeListener) {
        this.mOnVisibleChangeListener = mOnVisibleChangeListener;
        return this;
    }

    public AnyLayer onLayerShowListener(OnLayerShowListener mOnLayerShowListener) {
        this.mOnLayerShowListener = mOnLayerShowListener;
        return this;
    }

    public AnyLayer onLayerDismissListener(OnLayerDismissListener mOnLayerDismissListener) {
        this.mOnLayerDismissListener = mOnLayerDismissListener;
        return this;
    }

    public AnyLayer onClick(@NonNull OnLayerClickListener listener, @IdRes int viewId) {
        mViewHolder.addOnClickListener(listener, viewId);
        return this;
    }

    public AnyLayer onClick(@NonNull OnLayerClickListener listener, @IdRes int viewId, @IdRes int... viewIds) {
        mViewHolder.addOnClickListener(listener, viewId, viewIds);
        return this;
    }

    public AnyLayer onClickToDismiss(@IdRes int viewId, @IdRes int... viewIds) {
        mViewHolder.addOnClickListener(new OnLayerClickListener() {

            @Override
            public void onClick(AnyLayer anyLayer, View v) {
                dismiss();
            }
        }, viewId, viewIds);
        return this;
    }
    //endregion

    //region 接口及常量

    /**
     * 方向
     */
    public enum Direction {
        TOP, BOTTOM, LEFT, RIGHT
    }

    public interface IAnim {
        long inAnim(View targetView);

        long outAnim(View targetView);
    }

    public interface IDataBinder {
        void bind(AnyLayer anyLayer);
    }

    public interface OnLayerClickListener {
        void onClick(AnyLayer anyLayer, View view);
    }

    public interface OnLayerShowListener {
        void onShowing(AnyLayer anyLayer);

        void onShown(AnyLayer anyLayer);
    }

    public interface OnLayerDismissListener {
        void onDismissing(AnyLayer anyLayer);

        void onDismissed(AnyLayer anyLayer);
    }

    public interface OnVisibleChangeListener {
        void onShow(AnyLayer anyLayer);

        void onDismiss(AnyLayer anyLayer);
    }
    //endregion

}
